<!--
  form表单公共组件     zls-2022/06/01
      Attributes
          1、model              校验项的名称集合（它是一个对象）
          2、rules              验证规则（详见https://www.cnblogs.com/wozho/p/10955525.html）

      Methods（使用form组件时，绑定ref调用）
          1、reset              清空
          2、validate           提交（支持 Promise / callback）
      
      Slot

      Demo==>


-->
<template>
  <div class="z-form">
    <slot></slot>
  </div>
</template>

<script>
export default { name: 'z-form' }
</script>
<script setup>
import { provide, reactive, getCurrentInstance } from 'vue'
import { getType } from '@/utils'
const global = getCurrentInstance().appContext.config.globalProperties
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
  // 校验项的名称集合（它是一个对象）
  modelValue: {
    type: Object,
    required: true,
    default: () => ({})
  },
  // 验证规则
  rules: {
    type: Object,
    default: () => ({})
  }
})

// 向子组件提供当前组件实例
provide('form', { model: props.modelValue, rules: props.rules })

const modelNew = reactive(props.modelValue) // 保存所有表单项，用于清空操作
const fields = reactive([]) // 储存当前的 form-item的实例

// 存当前实例
global.$bus.on('formItemMounted', item => {
  if (item) fields.push(item)
})

// 删除当前有的实例(如果当前没有prop的话表示当前不要进行删除（因为没有注入）)
global.$bus.on('formItemDestroy', item => {
  if (item.prop) fields.splice(fields.indexOf(item), 1)
})

// 清空
function reset() {
  const obj = {
    number: '',
    string: '',
    undefined: undefined,
    null: null,
    object: null,
    array: null
  }
  Object.entries(modelNew).forEach(([key, value]) => {
    const type = getType(value)
    modelNew[key] = obj[type]
    emit('update:modelValue', modelNew)
  })
}

// 校验所有form-item 公开方法：全部校验数据，支持 Promise / callback
function validate(callback) {
  return new Promise(resolve => {
    let valid = true // 默认是通过
    let count = 0 // 来匹配当前是否是全部检查完
    fields.forEach(field => {
      // 每个实例都会有 validation 的校验的方法
      field.validation('', '', validateMessage => {
        // 只要有一个不符合那么当前的校验就是未通过的
        if (validateMessage) {
          valid = false
        }
        // 通过当前检查完所有的form-item的时候才会调用
        if (++count === fields.length) {
          typeof callback === 'function' ? callback(valid) : resolve(valid)
        }
      })
    })
  })
}
defineExpose({ reset, validate })
</script>

<style lang="scss" scoped>
.z-form {
  padding: 0 15px;
}
</style>
